package com.hundun.vision.biz.service.impl;

import com.hundun.vision.biz.mapper.BackTestMapper;
import com.hundun.vision.biz.po.BackTestPO;
import com.hundun.vision.biz.service.BackTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.ta4j.core.AnalysisCriterion;
import org.ta4j.core.BarSeries;
import org.ta4j.core.Strategy;
import org.ta4j.core.Trade.TradeType;
import org.ta4j.core.TradingRecord;
import org.ta4j.core.analysis.cost.CostModel;
import org.ta4j.core.analysis.cost.FixedTransactionCostModel;
import org.ta4j.core.analysis.cost.ZeroCostModel;
import org.ta4j.core.backtest.BarSeriesManager;
import org.ta4j.core.criteria.MaximumDrawdownCriterion;
import org.ta4j.core.criteria.PositionsRatioCriterion;
import org.ta4j.core.criteria.ReturnOverMaxDrawdownCriterion;
import org.ta4j.core.criteria.pnl.ReturnCriterion;
import org.ta4j.core.num.Num;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * @author ：RuoChen
 * @date ：10:28 2021/6/25
 * @description：回测服务实现类
 */
@Slf4j
@Component
public class BackTestServiceImpl implements BackTestService {

    @Resource
    private BackTestMapper backTestMapper;

    @Override
    public void test(Strategy strategy, TradeType tradeType, BarSeries barSeries, String symbol, String interval, BigDecimal stopLoss) {
        if (barSeries != null && barSeries.getBarCount() > 0) {
            CostModel transactionCostModel = new FixedTransactionCostModel(0.0005);
            CostModel holdingCostModel = new ZeroCostModel();
            BarSeriesManager seriesManager = new BarSeriesManager(barSeries, transactionCostModel, holdingCostModel);
            TradingRecord tradingRecord = seriesManager.run(strategy, tradeType);
            if (tradingRecord != null) {
                AnalysisCriterion returnCriterion = new ReturnCriterion();
                AnalysisCriterion maxWithdrawCriterion = new MaximumDrawdownCriterion();
                AnalysisCriterion profitWithdrawCriterion = new ReturnOverMaxDrawdownCriterion();
                AnalysisCriterion winRatioCriterion = new PositionsRatioCriterion(AnalysisCriterion.PositionFilter.PROFIT);
                Num returnResult = returnCriterion.calculate(barSeries, tradingRecord);
                Num maxWithdrawResult = maxWithdrawCriterion.calculate(barSeries, tradingRecord);
                Num profitWithdrawResult = profitWithdrawCriterion.calculate(barSeries, tradingRecord);
                Num winResult = winRatioCriterion.calculate(barSeries, tradingRecord);
                BackTestPO backTestPO = new BackTestPO();
                backTestPO.setSymbol(symbol);
                backTestPO.setTimes(tradingRecord.getPositionCount());
                backTestPO.setTimeInterval(interval);
                backTestPO.setStrategyName(strategy.getName());
                backTestPO.setStopLoss(stopLoss);
                if (!returnResult.isNaN()) {
                    BigDecimal profitRatio = returnResult.bigDecimalValue()
                            .subtract(BigDecimal.ONE)
                            .multiply(BigDecimal.valueOf(100))
                            .setScale(2, RoundingMode.DOWN);
                    backTestPO.setProfitRatio(profitRatio);
                }
                if (!maxWithdrawResult.isNaN()) {
                    BigDecimal maxWithdrawRatio = maxWithdrawResult.bigDecimalValue().multiply(BigDecimal.valueOf(100))
                            .setScale(2, RoundingMode.DOWN);
                    backTestPO.setMaxWithdrawRatio(maxWithdrawRatio);
                }
                if (!profitWithdrawResult.isNaN()) {
                    BigDecimal profitWithdrawRatio = profitWithdrawResult.bigDecimalValue()
                            .setScale(2, RoundingMode.DOWN);
                    backTestPO.setProfitWithdrawRatio(profitWithdrawRatio);
                }
                if (!winResult.isNaN()) {
                    BigDecimal winRatio = winResult.bigDecimalValue()
                            .setScale(2, RoundingMode.DOWN)
                            .multiply(BigDecimal.valueOf(100));
                    backTestPO.setWinRatio(winRatio);
                }
                backTestMapper.insertSelective(backTestPO);
            }
        }
    }
}
